package br.com.foxinline.util;

import br.com.foxinline.enums.FormaArrecadacao;
import br.com.foxinline.enums.FormaPagamento;
import br.com.foxinline.modelo.DadosIptu;
import br.com.foxinline.modelo.Parcela;
import br.com.foxinline.servico.DadosIptuServico;
import br.com.foxinline.servico.ParcelaServico;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Processa o arquivo de retorno (*.ret) do Banco e grava as informações
 * pertinentes as parcelas que foram pagas. Apenas para IPTU, ITBI e REQUERIMENTO
 *
 * @author laverson
 */
public class ProcessarArquivoRetorno {

    ParcelaServico parcelaServico;
    DadosIptuServico dadosIptuServico;
    private InputStream stream;
    private String nomeArquivoRetorno;
    private String codigoBanco;
    private Date dataGeracaoArquivo;
    private String nomeBanco;

    public ProcessarArquivoRetorno(InputStream stream, String nomeArquivoRetorno, ParcelaServico parcelaServico, DadosIptuServico dadosIptuServico) {

        this.stream = stream;
        this.nomeArquivoRetorno = nomeArquivoRetorno;
        this.parcelaServico = parcelaServico;
        this.dadosIptuServico = dadosIptuServico;
    }

    /**
     * Processa o arquivo de retorno e atualiza todas as parcelas, de IPTU e
     * ITBI, que foram pagas
     */
    public void processarArquivo() {

        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(this.stream));
            processarHeader(br);

            br.close();

        } catch (IOException ex) {

            Msg.messagemError(Msg.Failure);
            System.err.println(ex);
        }
    }

    /**
     * Processa a primeira linha do arquivo de retorno
     *
     * @param br
     */
    private void processarHeader(BufferedReader br) {

        try {
            String linha = br.readLine();

            // Valida a primeira linha do arquivo
            if (!validarLinhaHeader(linha)) {
                Msg.messagemError("REGISTRO \"A\" - HEADER - Erro de validação !");
                return;
            }

            this.codigoBanco = linha.substring(42, 45);
            this.dataGeracaoArquivo = formatarData(linha.substring(65, 73));

//            System.err.println("Código de registro: " + linha.substring(0, 1));
//            System.err.println("Código de Remessa: " + linha.substring(1, 2));
//            System.err.println("Código do Convênio: " + linha.substring(2, 22));
//            System.err.println("Nome da Empresa / Órgão: " + linha.substring(22, 42));
//            System.err.println("Código do Banco: " + linha.substring(42, 45));
//            System.err.println("Nome do Banco: " + linha.substring(45, 65));
            this.nomeBanco = linha.substring(45, 65);
//            System.err.println("Data da geração: " + linha.substring(65, 73).substring(0, 4) + "/" + linha.substring(65, 73).substring(4, 6) + "/" + linha.substring(65, 73).substring(6, 8));
//            System.err.println("Número sequencial: " + linha.substring(73, 79));
//            System.err.println("Versão do layout: " + linha.substring(79, 81));
//            System.err.println("CÓDIGO DE BARRAS: " + linha.substring(81, 98));
//            System.err.println("Reservado para o futuro: " + linha.substring(98, 150));

        } catch (IOException ex) {

            System.err.println(ex);
        }

        processarRetorno(br);
    }

    private void processarRetorno(BufferedReader bufferedReader) {

        try {
            String linha;
            String agenciaBanco;
            String contaBanco;
            String codigoBarras;
            Date dataPagamento;
            Date dataCredito;
            Date dataVencimentoParcela;
            String nossoNumero;
            String valorRecebido;
            String valorTarifa;
            String codigoAgenciaArrecadora;

            String codigoImposto;
            String idParcela;
            String tipoParcela;

            Parcela parcela;

            while (bufferedReader.ready()) {
                linha = bufferedReader.readLine();

                if (!validarLinhaRetorno(linha) && linha.substring(0, 1).equalsIgnoreCase("Z")) {
                    return;
                }

                codigoBarras = linha.substring(37, 81);
                nossoNumero = codigoBarras.substring(19, 44);

                codigoImposto = nossoNumero.substring(8, 11);

                // Processa apenas os registros referentes ao IPTU (código = 001), ITBI (código = 005) e REQUERIMENTO (código = 555)
                if (!"001".equals(codigoImposto) && !"005".equals(codigoImposto) && !"555".equals(codigoImposto)) {
                    continue;
                }

                dataVencimentoParcela = formatarData(nossoNumero.substring(0, 8));
                idParcela = nossoNumero.substring(11, 23);
                tipoParcela = nossoNumero.substring(23, 25);

                agenciaBanco = linha.substring(1, 21).substring(0, 5);
                contaBanco = linha.substring(1, 21).substring(5, 15);

                dataPagamento = formatarData(linha.substring(21, 29));
                dataCredito = formatarData(linha.substring(29, 37));

                valorRecebido = linha.substring(81, 91).concat(".").concat(linha.substring(91, 93));
                valorTarifa = linha.substring(93, 98).concat(".").concat(linha.substring(98, 100));
                codigoAgenciaArrecadora = linha.substring(108, 116);

                // -----------------------------------------------------------------//
                try {
                    parcela = parcelaServico.find(Long.parseLong(idParcela));

                    if (dataVencimentoParcela == parcela.getDataVencimento()) {

                        parcela.setCodigoImposto(Integer.parseInt(codigoImposto));

                        parcela.setFormaArrecadacao(formaArrecadacao(linha.substring(116, 117)));

                        parcela.setNomeArquivo(this.nomeArquivoRetorno);

                        parcela.setValorRecebido(new BigDecimal(valorRecebido));
                        parcela.setValorTarifa(new BigDecimal(valorTarifa));

                        if (!"005".equals(codigoImposto)) {
                            parcela.setDesconto(descontoParcelaUnica(tipoParcela));
                        }

                        parcela.setCodigoAgencia(codigoAgenciaArrecadora);
                        parcela.setCodigoBanco(this.codigoBanco);
                        parcela.setNomeBanco(this.nomeBanco);

                        parcela.setDataGeracaoArquivo(this.dataGeracaoArquivo);
                        parcela.setDataPagamento(dataPagamento);
                        parcela.setDataCredito(dataCredito);

                        parcela.setAgenciaBanco(agenciaBanco);
                        parcela.setContaBanco(contaBanco);
                        parcela.setPago(true);

                        parcelaServico.update(parcela);
                    }

                } catch (Exception e) {

                    System.err.println(e);
                }

//                System.err.println("Data da parcela: " + dataParcela);
//
//                System.err.println("Código do registro: " + linha.substring(0, 1));
//                System.err.println("agência/conta/dígito: " + agencia + "-" + digitoAgencia + " / " + conta + "-" + digitoConta);
//                System.err.println("Data de pagamento: " + dataPagamento);
//                System.err.println("Data de crédito: " + dataCredito);
//                System.err.println("Código de Barras: " + codigoBarras);
//                System.err.println("Nosso número: " + nossoNumero);
//                System.err.println("Valor recebido: " + valorRecebido);
//                System.err.println("Valor da tarifa: " + valorTarifa);
//                System.err.println("NSR - Número Sequencial de Registro: " + linha.substring(100, 108));
//                System.err.println("Código da agência arrecadadora: " + codigoAgenciaArrecadora);
//                System.err.println("Forma de arrecadação/captura: " + formaArrecadacao(linha.substring(116, 117)).getValor());
//                System.err.println("Número de autenticação caixa ou código de transação: " + linha.substring(117, 140));
//                System.err.println("Forma de Pagamento: " + linha.substring(140, 141));
//                System.err.println("Reservado para o futuro: " + linha.substring(141, 150));
//                System.err.println("----------------------------------------------------------------------");
            }

        } catch (IOException ex) {

            System.err.println(ex);
        }
    }

    private void processarRegistroTrailler(String linha) {

        System.err.println("Código do Registro: " + linha.substring(0, 1));
        System.err.println("Total de registros do arquivo: " + linha.substring(1, 7));
        System.err.println("Valor total recebido dos registros do arquivo: " + linha.substring(7, 24));
        System.err.println("Reservado para o futuro: " + linha.substring(24, 150));
        System.err.println("----------------------------------------------------------------------");
    }

    /**
     * Valida a primeira linha do arquivo de retorno, verificando os principais
     * campos.
     *
     * @param linha
     * @return verdadeiro se a linha for válida
     */
    private boolean validarLinhaHeader(String linha) {

        if (linha.length() != 150) {
            return false;
        }

        // Verificar Código de registro
        if (!linha.substring(0, 1).equalsIgnoreCase("A")) {
            return false;
        }

        // Verificar Código de Remessa
        if (!isLong(linha.substring(1, 2))) {
            return false;
        }

        // Verificar Código do Convênio
        if (!isLong(linha.substring(2, 22).trim())) {
            return false;
        }

        // Verificar Código do Banco
        if (!isLong(linha.substring(42, 45))) {
            return false;
        }

        // Verificar Data da geração do arquivo
        if (formatarData(linha.substring(65, 73)) == null) {
            return false;
        }

        // Verificar Número sequencial
        if (!isLong(linha.substring(73, 79))) {
            return false;
        }

        // Verificar Versão do layout
        if (!isLong(linha.substring(79, 81))) {
            return false;
        }

        return true;
    }

    /**
     * Valida a linha de retorno, verificando os campos mais importantes
     *
     * @param linha a ser velidade
     * @return verdadeiro se a linha for válida
     */
    private boolean validarLinhaRetorno(String linha) {

        if (linha.length() != 150) {
            return false;
        }

        // Verificar Código de registro
        if (!linha.substring(0, 1).equalsIgnoreCase("G")) {
            return false;
        }

        // Verificar agência/conta/dígito
        if (!isLong(linha.substring(1, 21).substring(0, 4)) && !isLong(linha.substring(1, 21).substring(5, 15).trim())) {
            return false;
        }

        // Verificar Data de pagamento
        if (formatarData(linha.substring(21, 29)) == null) {
            return false;
        }

        // Verificar Data de crédito
        if (formatarData(linha.substring(29, 37)) == null) {
            return false;
        }

        // Verificar Código de Barras
        if (!isLong(linha.substring(37, 81))) {
            return false;
        }

        // Verificar Data de vencimento da fatura
        if (formatarData(linha.substring(37, 81).substring(0, 8)) == null) {
            return false;
        }

        // Verificar forma de arrecadação
        if (formaArrecadacao(linha.substring(116, 117)) == null) {
            return false;
        }

        // Verificar forma de pagamento
        if (formaPagamento(linha.substring(140, 141)) == null) {
            return false;
        }

        return true;
    }

    /**
     * Converte uma <b>String<b/> no formato 'yyyyMMdd' para o tipo
     * java.util.Date
     *
     * @param data no formato 'yyyyMMdd'
     * @return null caso ocorra erro ao realizar o parse
     */
    private Date formatarData(String data) {

        try {

            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
            return sdf.parse(data);

        } catch (ParseException ex) {
            Logger.getLogger(ProcessarArquivoRetorno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }

    /**
     * Verifica se a forma de arrecadação é válida
     *
     * @param forma
     * @return null caso não seja válida
     */
    private FormaArrecadacao formaArrecadacao(String forma) {

        for (FormaArrecadacao formaArrecadacao : FormaArrecadacao.values()) {
            if (formaArrecadacao.getValor().equals(forma)) {
                return formaArrecadacao;
            }
        }

        return null;
    }

    /**
     * Verifica se a arrecadação é válida
     *
     * @param linha
     * @return null caso não seja válida
     */
    private FormaPagamento formaPagamento(String linha) {

        for (FormaPagamento formaPagamento : FormaPagamento.values()) {
            if (formaPagamento.getValor().equals(linha)) {
                return formaPagamento;
            }
        }

        return null;
    }

    /**
     * Obtém o valor desconto contido em dadosIptu para as parcelas únicas
     *
     * @param tipoParcela igual a 0(zero) para a parcela única e [1-99] para as
     * demais
     * @return null caso não seja a parcela única
     */
    private BigDecimal descontoParcelaUnica(String tipoParcela) {

        if (Integer.parseInt(tipoParcela) == 0) {

            DadosIptu dadosIptu = dadosIptuServico.find();

            return dadosIptu.getDesconto();
        }

        return null;
    }

    /**
     * Verifica se a string informada por parâmetro é um Long
     *
     * @param valor
     * @return true caso seja Long
     */
    private boolean isLong(String valor) {

        try {

            Long.parseLong(valor);

            return true;

        } catch (NumberFormatException numberFormatException) {

            return false;
        }
    }
}
